/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID coidgen tool.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Robo Strycek
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */



#include "coid/comm/commassert.h"
#include "coidgen.h"
#include "os.h"

using namespace coid;


enum template_parts {
	tp_includes=0, tp_version, tp_fnc_enum_start, tp_fnc_enum_end,
	tp_fnc_info0, tp_fnc_info_end,
	tp_descriptor0,
	tp_fnc_body0, tp_fnc_body, tp_fnc_body2,

    tp_getmeta, tp_getmeta2, tp_getmeta_fn, tp_getmeta_fn2,
    tp_getmeta_fn_noimpl_accept, tp_getmeta_fn_noimpl_accept_shared,

	tp_dispatch0, tp_dispatch1, tp_connect_case,
	tp_connect, tp_connect_shared, tp_connect_auto, tp_accept_net_connections_dest,
	tp_destroy, tp_command, tp_spawn, tp_echo,
	tp_dispatch_end,
	tp_std_fnc_not_impl, tp_connect_fnc_not_impl, tp_std_fnc_command, tp_std_fnc_interface, tp_std_fnc_interface_end,

	tp_method_table0, tp_method_table1,
	tp_get_method_id, tp_accept_net_connections,

	tp_last
};


void H_FILE::write_finfo( charstr& out, charstr& method_desc, const CLASS::FUNCTION* fnc )
{
	bool streamed = false;
	charstr flags;

	if( fnc->flags & FUNCTION::fdirect ) {if(streamed) {flags << " | ";} flags << "MethodDesc::fACCESS_MODE_DIRECT"; streamed = true;}
	if( fnc->flags & FUNCTION::finterprocess ) {if(streamed) {flags << " | ";} flags << "MethodDesc::fACCESS_MODE_INTERPROCESS"; streamed = true;}
	if( fnc->flags & FUNCTION::fremote ) {if(streamed) {flags << " | ";} flags << "MethodDesc::fACCESS_MODE_REMOTE"; streamed = true;}
	if( fnc->flags & FUNCTION::fconst ) {if(streamed) {flags << " | ";} flags << "MethodDesc::fCONST"; streamed = true;}
	if( fnc->flags & FUNCTION::fmeta ) {if(streamed) {flags << " | ";} flags << "MethodDesc::fMETA"; streamed = true;}
	if( fnc->flags & FUNCTION::fnolock ) {if(streamed) {flags << " | ";} flags << "MethodDesc::fNO_LOCK"; streamed = true;}
	if( !(fnc->flags & FUNCTION::fnolog) ) {if(streamed) {flags << " | ";} flags << "MethodDesc::fLOG_CALL"; streamed = true;}

	if( !streamed ) out << "0";

    charstr tmp;
	fnc->get_id_name( tmp );
    tmp << "_finfo";
	method_desc << "\tCOID_MethodDesc_constructor( " << tmp << ", 0x";
    method_desc.append_num(16, fnc->id, 8, ALIGN_NUM_RIGHT_FILL_ZEROS);
    method_desc << ", " << flags << " )";
	out << "static const char * " << tmp << "[] = {";
	out << "\"" << fnc->name << "\", \"";
	fnc->ret.stream_to( out, T_dispatch_cpp ); out << "\"";

	const dynarray<ARG>& args = fnc->args;
	uint mask = ARG::fin | ARG::finout;
	do {
		out << ", ";
		for( uint j=0; j<args.size(); j++ ) {
			if( args[j].flags & mask ) {
				out << "\"";
				args[j].stream_to( out, T_dispatch_cpp );
				out << "\", ";
			}
		}
		out << "NULL";

		if( mask == (ARG::fin | ARG::finout) )
            mask = ARG::fout | ARG::finout | ARG::ffetch;
		else break;
	} while( 1 );


	out << "};" EOL;
}

int H_FILE::write_dispatch_cpp( char * templ, charstr& out, uint class_index )
{
	if( class_index >= cls.size() )
		return 1;
	CLASS * c = cls[class_index];

	templ_parsed.reset();
	char * ptr = templ;

	while( 1 ) {
		char * p = strstr( ptr, "$$$$" );
		if( p ) *p = 0;
		*templ_parsed.add(1) = ptr;

		if( ! p ) break;
		ptr = p + 4;
		if( *ptr == '\r' ) ptr++;
		if( *ptr == '\n' ) ptr++;
	}



	if( templ_parsed.size() < tp_last ) {
		printf( "Invalid template ! %ld / %d\n", templ_parsed.size(), tp_last );
		DASSERT( 0 && "Invalid template !" );
		return 1;
	}
	else if( templ_parsed.size() > tp_last )
		printf( "There are too many template subsections ! (%ld : %d)\n", templ_parsed.size(), tp_last );


	out << EOL EOL;


	/// basic includes, #defines
	replace_template( true, templ_parsed[tp_includes], out, c );

	/// basic constants, version, name, ...
	replace_template( true, templ_parsed[tp_version], out, c );

	/// enum
	replace_template( true, templ_parsed[tp_fnc_enum_start], out, c );
	dynarray<FUNCTION*> & fnc = c->fnc_export;
	charstr tmp;
	uint i;
	for( i=0; i<fnc.size(); i++ ) {
		fnc[i]->get_id_name( tmp );
		out += "\t";
		out += tmp;
		out += " = ";
		out += fnc[i]->id;
        out += "," EOL;
	}
	out += EOL;
	replace_template( true, templ_parsed[tp_fnc_enum_end], out, c );



//////////////////// fnc info arrays /////////////////////////////////////////////////////
	charstr method_desc;
	replace_template( true, templ_parsed[tp_fnc_info0], method_desc, c );

	/// functions are sorted by id
	uint f=0,count;
	for( i=0, count=0; i<c->max_fnc_index; i++ ) {
		if(count) method_desc << "," EOL;
		if( f >= fnc.size() || fnc[f]->id != i ) {				/// fnc is not implemented
			method_desc << "\tCOID_MethodDesc_constructor_NotImpl";
			continue;
		}
		else {
            write_finfo( out, method_desc, fnc[f] );
			f++;
		}
		count++;
	}
    if(count) method_desc << "," EOL;
    write_finfo( out, method_desc, c->fnc_special[CLASS::accept_connect] );
    method_desc << "," EOL;
    write_finfo( out, method_desc, c->fnc_special[CLASS::accept_connect_shared] );
	method_desc << EOL;
	replace_template( true, templ_parsed[tp_fnc_info_end], method_desc, c );

	out << method_desc;


//////////////////// struct ServiceDescriptor /////////////////////////////////////////////////////

	replace_template( true, templ_parsed[tp_descriptor0], out, c );

//////////////////// getmeta function /////////////////////////////////////////////////////////////

    replace_template( true, templ_parsed[tp_getmeta], out, c );

    f = CLASS::accept_connect;
    CLASS::FUNCTION* sf = c->fnc_special[f];
    if( !sf || (sf->flags & FUNCTION::fdispatch_empty_body) )
        replace_template( true, templ_parsed[tp_getmeta_fn_noimpl_accept], out, c, 0x80000000 | f );
    else if( (sf->flags & FUNCTION::fcomm_mask) != FUNCTION::fdirect  &&  (sf->flags & FUNCTION::fmeta) )
    {
		replace_template( true, templ_parsed[tp_getmeta_fn], out, c, 0x80000000 | f );
		replace_template( true, templ_parsed[tp_getmeta_fn2], out, c, 0x80000000 | f );
    }

    f = CLASS::accept_connect_shared;
    sf = c->fnc_special[f];
    if( !sf || (sf->flags & FUNCTION::fdispatch_empty_body) )
        replace_template( true, templ_parsed[tp_getmeta_fn_noimpl_accept_shared], out, c, 0x80000000 | f );
    else if( (sf->flags & FUNCTION::fcomm_mask) != FUNCTION::fdirect  &&  (sf->flags & FUNCTION::fmeta) )
    {
		replace_template( true, templ_parsed[tp_getmeta_fn], out, c, 0x80000000 | f );
		replace_template( true, templ_parsed[tp_getmeta_fn2], out, c, 0x80000000 | f );
    }

	// add function calls for every function
	for( f=0; f<fnc.size(); f++ ) {
		if( fnc[f]->flags & FUNCTION::fdispatch_empty_body ) continue;
		if( (fnc[f]->flags & FUNCTION::fcomm_mask) == FUNCTION::fdirect ) continue;
		if( !(fnc[f]->flags & FUNCTION::fmeta) ) continue;

		replace_template( true, templ_parsed[tp_getmeta_fn], out, c, f );
		replace_template( true, templ_parsed[tp_getmeta_fn2], out, c, f );
	}

    replace_template( true, templ_parsed[tp_getmeta2], out, c );

//////////////////// dispatch function ////////////////////////////////////////////////////////////

	replace_template( true, templ_parsed[tp_dispatch0], out, c );

	if( c->fnc_special[CLASS::accept_startup_params] ) {
		out << "\t\t\t_coid_err_code = _coid_" << c->name << "_obj->" << STR_special_functions[CLASS::accept_startup_params].name
			<< "( " << "*_coid_inst_data->_cmdin );" EOL;
	}
    else
        out << "\t\t\t_coid_err_code = 0;" EOL;
	replace_template( true, templ_parsed[tp_dispatch1], out, c );

	/// code responsible for creating function pointer table
	replace_template( true, templ_parsed[tp_std_fnc_interface], out, c );
	uint id = 0;
	for( i=0; i<c->max_fnc_index; i++ ) {
		if( id < c->fnc_export.size() && i == c->fnc_export[id]->id ) {
			FUNCTION * ftmp = c->fnc_export[id];
			if( ftmp->handle_client_calls == 1 ) {
				out << "\t\t\t\t\tCOID_DISPATCH_CREATE_CALLER_HOOK( call_dispatch, " << i << ", coid_table_index++ )";
				out << ";  // " << c->fnc_export[id]->name << " contains fetch or remap args" EOL;
			}
			else {
				out << "\t\t\t\t\tCOID_DISPATCH_CREATE_HOOK( " << ftmp->name << ", " << i << ", coid_table_index++, ";
				ftmp->stream_to( out, T_function_hook );
				out << " )" EOL;
			}
			id++;
		}
	}
	replace_template( true, templ_parsed[tp_std_fnc_interface_end], out, c );




	/// the rest
	replace_template( true, templ_parsed[tp_connect_case], out, c );

	uint templ_offset = 0;
	FUNCTION ** fnc2 = c->fnc_special;

	for( f=0; f<CLASS::SPEC_FNC_COUNT; f++ ) {
		if( f == CLASS::accept_startup_params ) {templ_offset = 1; continue;}

		if( fnc2[f] == NULL || (fnc2[f]->flags & FUNCTION::fdispatch_empty_body) ) {
			if( f <= CLASS::accept_connect_shared )
				replace_template( true, templ_parsed[tp_connect_fnc_not_impl], out, c );
			else if( f == CLASS::accept_net_connections )
				out << "\t\t\treturn ersNOT_IMPLEMENTED;" EOL;
			else replace_template( true, templ_parsed[tp_std_fnc_not_impl], out, c );
			
		}
		else {
			int index = tp_fnc_body;
			if( f == CLASS::accept_command ) index = tp_std_fnc_command;
			else if( f == CLASS::accept_net_connections ) index = tp_accept_net_connections;

			replace_template( true, templ_parsed[index], out, c, 0x80000000 | f );
		}
		replace_template( true, templ_parsed[tp_connect + f - templ_offset], out, c );
	}

	/// add function calls for every function
	for( f=0; f<fnc.size(); f++ ) {
		if( fnc[f]->flags & FUNCTION::fdispatch_empty_body ) continue;
		///uncomment line below if dispatch should not recognize direct methods
		if( (fnc[f]->flags & FUNCTION::fcomm_mask) == FUNCTION::fdirect ) continue;

		replace_template( true, templ_parsed[tp_fnc_body0], out, c, f );
		replace_template( true, templ_parsed[tp_fnc_body], out, c, f );
		replace_template( true, templ_parsed[tp_fnc_body2], out, c, f );
	}


	/// close dispatch function
	replace_template( true, templ_parsed[tp_dispatch_end], out, c );


	/// write method table:
	replace_template( true, templ_parsed[tp_method_table0], out, c );

	// body of the table
	uints table_size = c->fnc_export.size() * 3;
	if( table_size % 2 ) table_size++;
	table_size = table_size >> 1;
	dynarray<uints> table;
	table.add( table_size );
	memset( table.ptr(), 0xFF, sizeof(uint) * table_size );


	hash<token> mha;
	/// create table
	for( i=0; i<c->fnc_export.size(); i++ ) {
		uints idx = mha(c->fnc_export[i]->name) % table_size;
		uints t;
		for( t=0; t<table_size; t++ ) {
			if( table[idx] == UMAXS ) {
				table[idx] = i;
				break;
			}
			idx++;
			if( idx == table_size ) idx = 0;
		}
		DASSERT( t < table_size );
	}

	/// write it
	for( i=0; i<table_size; i++ ) {
		out << "\t{";
		if( table[i] == UMAXS ) {
			out << "NULL, UMAX32";
		}
		else {
			out << "\"" << c->fnc_export[ table[i] ]->name;
			out << "\", ";
			c->fnc_export[ table[i] ]->get_id_name( tmp );
			out << tmp;
		}

		if( i < table_size-1 ) out << "}," EOL;
		else out << "}" EOL;
	}

	replace_template( true, templ_parsed[tp_method_table1], out, c );


	/// write get_method_id()
	replace_template( true, templ_parsed[tp_get_method_id], out, c );




	return 0;
}










void H_FILE::replace_template( bool dispatch_or_client, const char * templ, charstr & out, const CLASS * c, uint fnc_index ) const
{
	char * ptr = (char *) templ;
	char * p;

	do {
		uint i = UMAX32;
		p = strchr( ptr, '%' );
		if( p ) {
			for( i=0; i<STR_templates_size; i++ )
				if( strncmp(p, STR_templates[i].name, STR_templates[i].size) == 0 )
					break;

			if( i < STR_templates_size ) {
				out.add_from( ptr, p - ptr );
				p += STR_templates[i].size;
			}
			else {
				out.add_from( ptr, p + 1 - ptr );
				ptr = p + 1;
			}
		}
		else out += ptr;


		if( i < STR_templates_size ) {
			if( STR_templates[i].flags < 1000 ) {		/// common replacements
				switch( STR_templates[i].flags ) {		/// flags == id
					//name
					case 1: out += c->name; break;
					//shortname
					case 2: {
						//char tmp[8] = {0, 0, 0, 0, 0, 0, 0, 0};
						//strncpy( tmp, c->name, 7 );	//tmp[7] = 0;
						//out += tmp;
                        token t = c->name;
                        t.truncate(8);
                        out += t;
					} break;
                    //info
                    case 3: {
                        token ttmc = c->main_comment;
                        for(; !ttmc.is_empty(); )
                        {
                            ttmc.skip_char('/');
                            token n = ttmc.cut_left("\r\n", token::cut_trait_remove_all());
                            if( out.last_char() != ' ' && n.first_char() != ' ' )
                                out << char(' ');
                            out << n;
                        }
                        break;
                    }
					//service_header_name
					case 4: out += source_header_name; break;
					//client_header_name
					case 5: out += client_header_name; break;
                    //derived_from
                    case 6: out += c->name_right; break;
					//fnc_count
					case 10: out << c->fnc_export.size(); break;
					//fnc_max_count
					case 11: DASSERT( c->fnc_export.size() <= c->max_fnc_index ); out << c->max_fnc_index; break;
					//method_flags_max_count
					case 12: DASSERT( c->fnc_export.size() <= c->max_fnc_index ); if( c->max_fnc_index ) out << c->max_fnc_index; break;
					//_version_
					case 20: {
						charstr tmp;
						header.ver.get_internal_version( tmp );
						char * p = strrchr( (char*)tmp.ptr(), '.' );
						if(p) *p = 0;
						out << replace_char( (char*)tmp.ptr(), '.', '_' );
					} break;
					//version
					case 21: header.ver.get_internal_version( out ); break;
					//comm_version: "\"release\", major, minor, build"
					case 22: out << "\"" << header.ver._ver << "\", " << header.ver.get_major() << ", " << header.ver.get_minor() << ", " << header.ver._build;
					break;
					//coid_version
					case 23: out << "COID_VERSION"; break;
					//flags
					case 50: {
						//out += (c->flags & CLASS::f_service_flags_mask);
						bool streamed = false;

						if( ! (c->flags & CLASS::fnolog_syscalls) )  {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fLOG_CALL"; streamed = true;}

						if( c->flags & CLASS::fallowdirect ) {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fALLOW_DIRECT_ACCESS"; streamed = true;}
						if( c->flags & CLASS::fallowinterprocess ) {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fALLOW_INTERPROCESS_ACCESS"; streamed = true;}
						if( c->flags & CLASS::fallowremote ) {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fALLOW_REMOTE_ACCESS"; streamed = true;}

						if( c->flags & CLASS::fautonomous ) {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fAUTONOMOUS"; streamed = true;}
						if( c->flags & CLASS::fbindable ) {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fBINDABLE"; streamed = true;}
						//if( c->flags & CLASS::fshared ) {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fSHARED"; streamed = true;}
						if( c->flags & CLASS::fhas_spawn_point ) {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fHAS_SPAWN_POINT"; streamed = true;}
						if( c->flags & CLASS::fautorun ) {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fAUTORUN"; streamed = true;}

						if( c->flags & CLASS::facceptor_bound_session ) {if( streamed ) {out << " | ";} out << "ServiceDescriptor::fACCEPTOR_BOUND_SESSION"; streamed = true;}

						if( ! streamed ) out << "0";
					} break;
					// dependencies
					case 60: {
						const char * ptr = c->dependencies.ptr();
						if( ! ptr ) break;
						out << "\t";
						size_t offset = 0;
						while( *ptr ) {
							offset = strcspn( ptr, " \t,;" );
							out << "\"";
							out.add_from( ptr, offset );
							out << "\", ";
							ptr += offset;
							if( ! *ptr ) break;
							while( *ptr == ' ' || *ptr == '\t' || *ptr == ',' || *ptr == ';' )
								ptr++;
						}
					} break;
					//class_or_struct
					case 80:
						if( c->flags & CLASS::fstruct ) out << "struct";
						else out << "class";
					break;
					//namespace
					case 81:
                        out << c->name_space;
					break;
					//acceptor_port
                    case 90: {
                        char tmp[16];
                        sprintf( tmp, "%d", c->acceptor_port );
                        out << tmp;
                    } break;
					//fnc_method_flags
					case 100:
						/*if( c->fnc_export.size() == 0 ) out << "NULL";
						else*/
                            replace_template( dispatch_or_client, "COID_%name%_method_desc", out, c, fnc_index );
					break;
					//empty_method_set_start
					case 110: if( c->fnc_export.size() == 0 ) out << "/*" EOL; break;
					//empty_method_set_end
					case 111: if( c->fnc_export.size() == 0 ) out << "*/" EOL; break;
                    //declare_coid_flags_if_nonzero
                    case 116: {
                        uint i, count = 0;
                        for( i=0; i<c->max_fnc_index; i++ ) {
                            if( count < c->fnc_export.size() && i == c->fnc_export[count]->id ) {
                                count++;
                                break;
                            }
                        }
                        if( count )
                            out << "			const uint * coid_flags = coid_iface_info->_methflags;" EOL;
                    } break;
					//coid_throw_function
					case 117: {
						if( c->fnc_export.size() ) {
							out << "/// fake function, should never be called:" EOL;
							out << "static void coid_throw() {throw ersFE_EXCEPTION;}" EOL;
						}
					} break;
					//fnc_client_table
					case 200: {
						replace_template( false, STR_fnc_table_name_begin, out, c );
						/// disconnect function
						out << "\t";
						c->fnc_disconnect.stream_to( out, T_function_table );
						out << EOL;
						for( uint j=0; j<c->fnc_export.size(); j++ ) {
							out << "\t";
							c->fnc_export[j]->stream_to( out, T_function_table );
							out << EOL;
						}
						out << "};";
					} break;
					//mexestream
					case 210: {
						bool need_mexestream = false;
						for( uint i=0; i<c->fnc_export.size(); i++ ) {
							if( c->fnc_export[i]->handle_client_calls == 1 ) {
								need_mexestream = true;
								break;
							}
						}
						if( need_mexestream )
							out << "\tmutable mexestream _mexestream;" EOL;
					} break;

					default: printf( "Template parameter '%s' not implemented !\n", STR_templates[i].name ); DASSERT( 0 ); break;
				}
			}
			else {				/// FUNCTIONs
				if( dispatch_or_client )
                    replace_disp_template( i, out, c, fnc_index );
				else
                    replace_client_template( i, out, c, fnc_index );
			}
		}
		if( i < STR_templates_size ) ptr = p;
	} while( p );
}





void H_FILE::replace_disp_template( uint templ_index, charstr & out, const CLASS * c, uint fnc_index ) const
{
	FUNCTION * f;
	if( fnc_index & 0x80000000 ) {	// special fnc
		DASSERT( fnc_index != UMAX32 );
        uint index2 = fnc_index & ~0x80000000;
		DASSERT( index2 < CLASS::SPEC_FNC_COUNT );
		f = c->fnc_special[index2];
	}
	else {
		if( fnc_index >= c->fnc_export.size() )
			DASSERT( fnc_index < c->fnc_export.size() );
		f = c->fnc_export[fnc_index];
	}

    bool bmeta = (f->flags & CLASS::FUNCTION::fmeta) != 0;

	charstr tmp;
	f->get_id_name( tmp );
	ARG * a = f->args.ptr();

	switch( STR_templates[templ_index].flags ) {		/// flags == id
		/// fnc_id_name
		case 1000:
            out << tmp; break;
		/// fnc_get_params
		case 1010: {
			out << EOL;
			ARG * arg_count_tmp = NULL;
			for( uint i=0; i<f->args.size(); i++, a++ ) {
				if( a->count_string.len() && a->arg_count != UMAX32 && ! (a->flags & (ARG::ffetch | ARG::fout)) &&
					arg_count_tmp == NULL )
				{
					//count must be streamed before the contents
					arg_count_tmp = a;
					a = a->arg_count_eval;
					DASSERT( a );
				}
				else arg_count_tmp = NULL;

				out << "\t\t\t";
				tmp.reset();
				//skip "const" in argument type
				if( (a->flags & ARG::fconst) && !(a->flags & ARG::ffetch) ) {
					remove_const( a->type, tmp );
				}
				else tmp << a->type;


				int tmp_asterix_count = a->asterix_count + ((a->flags & ARG::fref) ? 1 : 0);
				if( (a->flags & (ARG::fin | ARG::fstring)) == (ARG::fin | ARG::fstring)  ||
					((a->flags & ARG::ffetch) && a->asterix_count) )
				{
					tmp << " *";
				}
				else if( (a->flags & ARG::fstring) && tmp_asterix_count == 2 ) tmp << " *";


				bool b_localarray = a->is_localarray();

				//write declaration
				if( b_localarray ) {
					out << "localarray< " << tmp << ", " << ((int) (a->flags & ARG::fmalloc) != 0);
					if( a->flags & (ARG::fout) ) out << ", true";
					out << " > ";
				}
                else if( tmp == "token" || tmp == "coid::token" )
                    out << "charstr ";
				else
                    out << tmp << " ";

				//name
				tmp.reset();
				a->append_dispatch_arg_name( tmp, ARG::adt_name );
				out << tmp << ";" EOL;

				//alloc()
				if( b_localarray && ! (a->flags & ARG::fout) ) {
					out << "\t\t\t" << tmp << ".alloc( (uint) ";
					a->append_dispatch_arg_name( out, ARG::adt_count_name );
					out << " );" EOL;
				}

				//stream it in
				if( a->flags & (ARG::fin | ARG::finout) ) {
					if( b_localarray ) {
						out << "\t\t\t_coid_bstr.read_compound_array( " << tmp << ".get_ptr(), (uint) ";
						a->append_dispatch_arg_name( out, ARG::adt_count_name );
						out << " );" EOL;
					}
					else {
                        charstr pcast;
                        if( a->type.begins_with("enum") && (a->type.nth_char(4)==' ' || a->type.nth_char(4)=='\t') ) {
                            pcast << "(EnumType<sizeof(";
                            a->append_client_arg_name( pcast, ARG::adt_name );
                            pcast << ")>::TEnum&)";
                        }
                        else if( a->flags & ARG::fnet_ptr )
                            pcast << STR_net_ptr_cast_dest;

						if(bmeta)
                            out << "\t\t\tif(coid_meta)  coid_meta->xstream_in(" << pcast << tmp
                                << ", \"" << a->name
                                << "\"); else  _coid_bstr >> ";
                        else
						    out << "\t\t\t_coid_bstr >> ";

                        out << pcast << tmp << ";" EOL;
					}
				}

				if( arg_count_tmp ) {
					a = arg_count_tmp - 1; i--;
				}
			}
		} break;
		/// fnc_get_params_meta
		case 1015: {

            bool is_opcd = f->opcd_return_type();
			if( is_opcd )
                out << "med->retval.meta = META_TD(opcd);" EOL;
			else if( !f->void_return_type() ) {
				out << "med->retval.meta = META_TD(" << f->ret.type;
				if( f->ret.flags & ARG::fptr )
                    out << "*";
				out << ");" EOL;
			}

			out << EOL;
            uint argi=0, argo=0;

			for( uint i=0; i<f->args.size(); i++, a++ )
            {
				out << "\t\t";
				tmp.reset();
				//skip "const" in argument type
				if( (a->flags & ARG::fconst) && !(a->flags & ARG::ffetch) )
					remove_const( a->type, tmp );
				else
                    tmp << a->type;

				int tmp_asterix_count = a->asterix_count + ((a->flags & ARG::fref) ? 1 : 0);
				if( (a->flags & (ARG::fin | ARG::fstring)) == (ARG::fin | ARG::fstring) )//  ||
					//(a->flags & ARG::ffetch) && a->asterix_count )
				{
					tmp << "*";
				}
				else if( (a->flags & ARG::fstring) && tmp_asterix_count == 2 )
                    tmp << "*";

                if( a->type.begins_with("enum") && (a->type.nth_char(4)==' ' || a->type.nth_char(4)=='\t') ) {
                    tmp.reset();
                    tmp << "EnumType<sizeof(" << a->type << ")>::TEnum";
                }

				//bool b_localarray = a->is_localarray();
                if( a->flags & (ARG::fin | ARG::finout | ARG::faccount) )
                    out << "med->inargs[" << argi++ << "].meta = META_TD(" << tmp << ");" EOL;
                if( a->flags & ARG::finout )
                    out << "\t\t";
                if(! (a->flags & (ARG::fin | ARG::faccount)) )
                    out << "med->outargs[" << argo++ << "].meta = META_TD(" << tmp << ");" EOL;
			}
		} break;		/// fnc_log
		case 1020: {
			int count = 0;
			if( fnc_index & 0x80000000 ) {
				char tmp2[64];
				sprintf( tmp2, "\"%lX\"", (ulong)f->id );
				tmp = tmp2;
			}
			out << "\"(\" << " << tmp << " << \") " << f->name << " ( \";";
			for( uint i=0; i<f->args.size(); i++, a++ ) {
				if( count ) out << " *_coid_log << \", \";";

				if( (a->flags & (ARG::fin | ARG::finout)) && (a->flags & ARG::fnolog) == 0 ) {	// && ! a->is_localarray()
					if( a->is_array() ) {
						out << " (*_coid_log).";
						a->stream_array( out );
					}
					else {
						out << " *_coid_log << ";
						a->append_dispatch_arg_name( out, ARG::adt_stream_out );
						out << ";";
					}
				}
				else
					out << " *_coid_log << \"?\";";

				count++;
			}
			out << " *_coid_log << \" )\"";
		} break;
		/// fnc_log_output
		case 1021: {
			int count = 0;
			out << "\"  -->  (\";";
			for( uint i=0; i<f->args.size(); i++, a++ ) {
				if( (a->flags & (ARG::finout | ARG::fout | ARG::ffetch)) ) {
					if( count ) out << " *_coid_log << \", \";";

					if( (a->flags & (ARG::finout | ARG::fout | ARG::ffetch)) && (a->flags & ARG::fnolog) == 0 ) {	// && ! a->is_localarray()
						if( a->is_array() ) {
							out << " (*_coid_log).";
							a->stream_array( out );
						}
						else {
							out << " *_coid_log << ";
							a->append_dispatch_arg_name( out, ARG::adt_stream_out );
							out << ";";
						}
					}
					else
						out << " *_coid_log << \"?\";";
					count++;
				}
			}
			out << " *_coid_log << \")\"";
		} break;
		/// fnc_call
		case 1030: {
			/// declare return value(s)
			bool is_opcd = f->opcd_return_type();
			if( is_opcd )
                out << "_coid_err_code = ";
			else if( !f->void_return_type() ) {
				out << f->ret.type;
				if( f->ret.flags & ARG::fptr )
                    out << " *";
				else if( f->ret.flags & ARG::fref )
                    out << " &";
				out << " " << STR_coid_return_value << " = ";
			}

			replace_template( true, "_coid_%name%_obj->", out, c, fnc_index );

			int count = 0;
			out << f->name << "( ";
			for( uint i=0; i<f->args.size(); i++, a++ ) {
				if( count ) out << ", ";
				a->append_dispatch_arg_name( out, ARG::adt_argument );
				count++;
			}
			out << " );" EOL "\t\t\t";

			/// check for accept_connect[_shared] method && set identity (if used)
			if( (fnc_index & ~0x80000000) <= CLASS::accept_connect_shared && f->get_account_arg() ) {
				const ARG * aaa = f->get_account_arg();
				out << "_coid_inst_data->_cmdin->set_identity( " << aaa->name << " );" EOL;
				out << "\t\t\t";
			}
			if( !is_opcd )
				out << "if( _coid_inst_data->_cmdin ) _coid_err_code = _coid_inst_data->_cmdin->error;" EOL "\t\t\t";

            if(bmeta)
                out << "if(coid_meta)  coid_meta->xstream_out(_coid_err_code,\"err\");  else  ";
			out << "_coid_bstr << _coid_err_code;";
		} break;

		/// fnc_get_results
		case 1040: {
			static const char STR_if_error__[] = "if( ! _coid_err_code ) {" EOL;

			int flgiferr = 0;
            //stream out custom return type
			if( !f->opcd_return_type() && !f->void_return_type() )  {
				out << STR_if_error__ << "\t\t\t\t";
                if(bmeta)
                    out << "if(coid_meta)  coid_meta->xstream_out( " << STR_coid_return_value << ", \"retval\" );  else  ";
				out << "_coid_bstr << " << STR_coid_return_value;
				out << ";" EOL;
				++flgiferr;
			}

			for( uint i=0; i<f->args.size(); i++, a++ )
            {
				if( a->flags & (ARG::fin | ARG::faccount) )
                    continue;
				if( flgiferr++ == 0 )
                    out << STR_if_error__;

				//if( a->count_string.len() && a->arg_count != UMAX32 )			// count must be streamed before the content
				//	out << "\t\t\t\t_coid_bstr << (uint) " << a->count_string << ";" EOL;

				int tmp_asterix_count = a->asterix_count + ((a->flags & ARG::fref) ? 1 : 0);
				if( (a->flags & ARG::fptr_count) && tmp_asterix_count == 2 )
                {
                    out << "\t\t\t\tbinstream_container_fixed_array< ";
                    remove_const( a->type, out );
                    out << " > C( ";
                    a->append_dispatch_arg_name( out, ARG::adt_name );
                    out << ", uint(";
                    out << a->count_string << ") );" EOL "\t\t\t\t";
                    if(bmeta)
                        out << "if(coid_meta)  coid_meta->xstream_out_array(C,\"" << a->name << "\");  else  ";
                    out << "_coid_bstr.write_array(C);" EOL;
/*
					out << "\t\t\t\t_coid_bstr.write_linear_array( ";
					if( a->flags & ARG::ffetch )
                        a->append_dispatch_arg_name( out, ARG::adt_stream_out );
					else {
						a->append_dispatch_arg_name( out, ARG::adt_name );
						out << ".get_ptr()";
					}
					out << ", uint(" << a->count_string << ") )";
*/
				}
				else {
                    charstr pcast;
                    if( a->type.begins_with("enum") && (a->type.nth_char(4)==' ' || a->type.nth_char(4)=='\t') ) {
                        pcast << "(EnumType<sizeof(";
                        a->append_dispatch_arg_name( pcast, ARG::adt_stream_out );
                        pcast << ")>::TEnum&)";
                    }
                    else
                        pcast = (a->flags & ARG::fnet_ptr) ? STR_net_ptr_cast_src : token::empty();

                    out << "\t\t\t\t";
                    if(bmeta) {
                        out << "if(coid_meta)  coid_meta->xstream_out( " << pcast;
					    a->append_dispatch_arg_name( out, ARG::adt_stream_out );
                        out << ", \"" << a->name << "\" );  else  ";
                    }

					out << "_coid_bstr << " << pcast;
					a->append_dispatch_arg_name( out, ARG::adt_stream_out );
				    out << ";" EOL;
				}
			}

			if( flgiferr )
                out << "\t\t\t}" EOL;

			/// skip flushing for connects
			if( (fnc_index & 0x80000000) && (fnc_index & 0x7FFFFFFF) <= CLASS::accept_connect_shared ) {}
			else out << EOL "\t\t\t_coid_bstr.flush();";
		} break;
		/// fnc_delete_tmp_objects
		case 1050: {
			for( uint i=0; i<f->args.size(); i++, a++ ) {
				if( a->flags & ARG::ffetch ) continue;
				tmp.reset();
				a->append_dispatch_arg_name( tmp, ARG::adt_name );
				if( (a->flags & (ARG::fstring | ARG::fin)) == (ARG::fstring | ARG::fin) ) {
					out << "delete [] " << tmp << ";" EOL "\t\t\t";
				}
			}
		} break;

		default: printf( "Template parameter '%s' not implemented !\n", STR_templates[templ_index].name ); DASSERT( 0 ); break;
	}
}







void ARG::append_dispatch_arg_name( charstr & out, ARG_DISPATCH_TYPE type ) const
{
	switch( type ) {
		case adt_delete:
		case adt_stream_in:
		break;

		case adt_count_name:
			if( arg_count == UMAX32 ) out << count_string; 
			else out << arg_count_eval->name;
		return;

		case adt_argument:
			if( flags & fin ) {
			}
			else if( flags & ffetch ) {
				if( asterix_count == 2 ) out << "&";
			}
			else {		/// finout | fout
				if( is_localarray() ) {
					if( ! (flags & fref) ) out << "&";
					out << name << ".get_ptr()";
					return;
				}
				else if( ! (flags & (fref | fptr_count)) ) out << "&";
			}
		break;

        case adt_dispatch_fcall:
            if( !(flags & ffetch) ) {
                out << name << ".get_ptr()";
                return;
            }
            //else the same as adt_stream_out
		
        case adt_stream_out:
			if( flags & ffetch ) {
				if( !(flags & fptr_count) ) out << "*";
			}
			else if( (flags & (fref | fptr | fstring )) == (fref | fptr) )
				out << "*";
		break;

		case adt_name: break;	// suppress warning
	}

	out << name;
}





void H_FILE::remove_const( const charstr & name, charstr & out )
{
	const char * p = name.ptr();
	const uint l = sizeof(STR_const) - 1;
	for( uint j=0; j<name.len(); j++, p++ ) {
		if( *p == *STR_const && (p[l]==' ' || p[l] <= '\r')
			&& strncmp(p,STR_const,sizeof(STR_const)-1) == 0 )
		{
			p += l; j += l;
			continue;
		}
		out << (char) *p;
	}
}





